package notice

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"

	"gitee.com/smewl13/beehive/pkg/lcrypto"
	"gitee.com/smewl13/beehive/pkg/lhttp"
	"github.com/spf13/viper"
	"go.uber.org/zap"
)

type WXWorkResponse struct {
	Code int    `json:"errcode"`
	Msg  string `json:"errmsg"`
}

func NewWXWork(logger *zap.Logger, conf *viper.Viper) *WXWork {
	subject := strings.TrimSpace(conf.GetString("notice.subject"))
	if len(subject) == 0 {
		subject = "最新VPN连接信息"
	}

	wx := &WXWork{
		logger:  logger,
		subject: subject,
		groups:  make([]*WXWorkGroup, 0),
	}

	encrypt := strings.TrimSpace(conf.GetString("wxwork.encrypt"))
	encrypt = strings.ToLower(encrypt)

	var method lcrypto.Encrypter
	switch encrypt {
	case "sm4":
		key := conf.GetString("sm4.key")
		method = lcrypto.NewSM4(key)
	case "":
		method = nil
	default:
		logger.Fatal("[企业微信通知]不支持的加密算法", zap.String("加密算法", encrypt))
	}

	groups, _ := conf.Get("wxwork.group").([]any)
	for _, item := range groups {
		group, _ := item.(map[string]any)
		wxGroup := NewWXWorkGroup(logger, group, method)
		if wxGroup == nil {
			continue
		}
		wx.groups = append(wx.groups, wxGroup)
	}

	return wx
}

type WXWork struct {
	status  bool
	subject string
	tpl     string
	groups  []*WXWorkGroup
	logger  *zap.Logger
}

func (wx *WXWork) Name() string {
	return "wxwork"
}

func (wx *WXWork) Status() bool {
	return wx.status
}

func (wx *WXWork) SetTpl(tpl string) {
	wx.tpl = tpl
}

func (wx *WXWork) Send(data string) {
	wx.status = false
	title := "[企业微信通知]发送失败"
	for _, group := range wx.groups {
		resp := group.Request("POST", nil, []byte(fmt.Sprintf(wx.tpl, wx.subject, data)))
		if resp == nil {
			wx.logger.Error(title, zap.String("接收群组", group.name))
			continue
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			wx.logger.Error(
				title,
				zap.String("接收群组", group.name),
				zap.Int("status_code", resp.StatusCode),
			)
			continue
		}

		b, err := io.ReadAll(resp.Body)
		if err != nil {
			wx.logger.Error(
				"[企业微信通知]读取响应失败",
				zap.String("接收群组", group.name),
				zap.Error(err),
			)
			continue
		}

		ret := &WXWorkResponse{}
		if err := json.Unmarshal(b, ret); err != nil {
			wx.logger.Error(
				"[企业微信通知]响应解析失败",
				zap.String("接收群组", group.name),
				zap.Error(err),
			)
			continue
		}

		if ret.Code != 0 {
			wx.logger.Error(
				title,
				zap.String("接收群组", group.name),
				zap.String("body", string(b)),
			)
			continue
		}

		wx.status = true
		wx.logger.Info("[企业微信通知]发送成功", zap.String("接收群组", group.name))
	}
}

func NewWXWorkGroup(
	logger *zap.Logger, group map[string]any, method lcrypto.Encrypter,
) *WXWorkGroup {
	name, _ := group["name"].(string)
	url, _ := group["url"].(string)
	url = strings.TrimSpace(url)
	if method != nil {
		buf, err := method.Decrypt([]byte(url))
		if err != nil {
			logger.Error(
				"[企业微信通知]群组url解密失败",
				zap.String("群组名", name),
				zap.String("加密算法", method.Name()),
				zap.String("密文url", url),
				zap.Error(err),
			)
			return nil
		}
		url = string(buf)
	}
	if len(url) == 0 {
		logger.Error("[企业微信通知]群组url无效", zap.String("群组名", name))
		return nil
	}

	return &WXWorkGroup{
		name:   name,
		Client: lhttp.NewClient(logger, url),
	}
}

type WXWorkGroup struct {
	*lhttp.Client
	name string
}
